# -*-coding=utf-8 -*-
"""
    @author:sirius
    @time:2017.8.14
"""
import math
import random
# 标准化数据模块
from sklearn import preprocessing 
import numpy as np
# 将资料分割成train与test的模块
from sklearn.model_selection import train_test_split
# 可视化数据的模块
import matplotlib.pyplot as plt

#算法流程：
#1、建立应用-用户的倒排表，表示应用被那些用户安装
#2、从目标用户安装的每一个应用开始遍历
#3、寻找和目标用户有共同安装应用交集的所有用户作为初始邻居
#4、对初始邻居里边的每一个邻居，计算和目标用户的余弦夹角相似度
#5、建立相似度到用户的倒排表，相似度从高到低排列
#6、根据相似度找出最近k邻居
#7、从最近k邻安装的所有的应用开始遍历
#  这里的推荐思路是：对于最近k邻居安装的所有应用中的某一应用m
#  如果m仅仅被一个邻居安装，那么目标用户对该应用的的兴趣度就是目标用户和这个邻居的相似度
#  如果m被多个邻居安装，那么目标用户对此应用的兴趣度为目标用户与这些邻居相似度之和
#8、建立目标用户兴趣度-应用id的倒排表
#9、根据兴趣度由大到小进行推荐 

#读取文件，读取以行为单位，每一行是列表里的一个元素
def readFile(filename):
     contents = []
     f = open(filename, "r")
     contents = f.readlines()
     f.close()
     return contents

#获取应用的列表
def getAppList(filename):
     contents = readFile(filename)
     apps_info = {}
     # line_num = 0  
     # for app in contents:
     	  # line_num += 1
     	  # if line_num != 1:
          	# single_info = app.split(",")
          	# apps_info[int(single_info[0])] = single_info[1:]
     for app in contents:
          single_info = app.split(",")
          apps_info[single_info[1]] = single_info[0]     

     return apps_info

#获取事件的列表
def getEventList(filename, userId):
     contents = readFile(filename)
     apps_info = []
     # line_num = 0  
     # for app in contents:
            # line_num += 1
            # if line_num != 1:
               # single_info = app.split(",")
               # apps_info[int(single_info[0])] = single_info[1:]
     for app in contents:
          single_info = app.split(",")  
          if single_info[2] == userId:
               apps_info.append(single_info[1])   

     return apps_info

#数据格式化为二维数组
def getRatingInfo(ratings):
     rates = []
     line_num = 0  
     for line in ratings:
          rate = line.split(",")
     	  # line_num += 1
     	  # if line_num != 1:
	          # rate = line.split(",")
			  #  app_events.csv
			  #  user id | app id | start-up time | run time | network consumption.
	          # rates.append([int(rate[0]), int(rate[1]), int(rate[2]), int(rate[3]), int(rate[4])])
          try:
               rates.append([rate[0], rate[1], rate[2], rate[3], rate[4], rate[5]])
          except Exception, e:
               rates.append([rate[0], rate[1], rate[2], rate[3], 0, 0])

     return rates

#生成用户评分数据结构
def getUserScoreDataStructure(rates):
     '''
	 listUser2Score[2]=[(23,2,0),(43,0,1),(36,1,2)....] 表示用户2的应用安装列表（apps list）23,42,36（AppID）
      用户2应用23电量消耗层级为2(高),网路消耗层级为0(低);应用43电量消耗层级为0(低),网路消耗层级为1(中);应用36电量消耗层级为1(中),网路消耗层级为2(高);
     '''
     listUser2Score = {}
	 # dictItem2Users{},key=item id,value=user id list
     dictItem2Users = {}
     for k in rates:
	 #  app_events.csv
	 #  user id | app id | start counts | runtime | network.
          user_rank = (k[0], k[1], k[4], k[5])
          if k[2] in listUser2Score:
               listUser2Score[k[2]].append(user_rank)
          else:
               listUser2Score[k[2]] = [user_rank]

          if k[1] in dictItem2Users:
            if k[2] not in dictItem2Users[k[1]]:
               dictItem2Users[k[1]].append(k[2])
          else:
               dictItem2Users[k[1]] = [k[2]]

     return listUser2Score, dictItem2Users

#计算与目标用户相似的k个用户 
def getNearestNeighbor(userId, listUser2Score, dictItem2Users):
     '''
      listUser2Score[2]=[(23,2,0),(43,0,1),(36,1,2)....] 表示用户2的应用安装列表（apps list）23,42,36（AppID）
      用户2应用23电量消耗层级为2(高),网路消耗层级为0(低);应用43电量消耗层级为0(低),网路消耗层级为1(中);应用36电量消耗层级为1(中),网路消耗层级为2(高);
     '''
     neighbors = []
	 # 对于目标用户userId的每一个周期律item
     for item in listUser2Score[userId]:
	 # dictItem2Users{},key=item id,value=user id list
	 # item[0]表示应用id,item[1]表示应用电量评分,item[2]表示应用网络评分
	 # dictItem2Users[item[0]]=dictItem2Users[应用id]=value=安装该应用的用户列表
	 # 从安装该应用的用户列表里，计算目标用户和这个列表里边所有用户的相似度
          for neighbor in dictItem2Users[item[1]]:
			# 如果这个邻居不是目标用户并且这个邻居还没有被加入邻居集就加进来
               if neighbor != userId and neighbor not in neighbors:
                    neighbors.append(neighbor)
     neighbors_dist = []
	# 里边存储的是[相似度，邻居id]
     for neighbor in neighbors:
          # listUser2Score[2]=[(23,2,0),(43,0,1),(36,1,2)....] 表示用户2的应用安装列表（apps list）23,42,36（AppID）
          # 用户2应用23电量消耗层级为2(高),网路消耗层级为0(低);应用43电量消耗层级为0(低),网路消耗层级为1(中);应用36电量消耗层级为1(中),网路消耗层级为2(高);
          dist = getCosDist(listUser2Score[userId], listUser2Score[neighbor])
          neighbors_dist.append([dist, neighbor])
		# 按照相似度倒排，相似度从高到低
     neighbors_dist.sort(reverse = True)
     print "序号\t用户ID\t用户相似度\t"
     for i in xrange(0,2):
     	print "%d\t%s\t%f\t" % (i+1, neighbors_dist[i][1], neighbors_dist[i][0])
     return neighbors_dist

# 计算余弦距离
# listUser2Score[2]=[(23,2,0),(43,0,1),(36,1,2)....] 表示用户2的应用安装列表（apps list）23,42,36（AppID）
# 用户2应用23电量消耗层级为2(高),网路消耗层级为0(低);应用43电量消耗层级为0(低),网路消耗层级为1(中);应用36电量消耗层级为1(中),网路消耗层级为2(高);
# dist = getCosDist(listUser2Score[userId], listUser2Score[neighbor])
def getCosDist(user1, user2):
     sum_x = 0.0
     sum_y = 0.0
     sum_xy = 0.0
     sum_xy1 = 0.0
     sum_xy2 = 0.0

     for key1 in user1:
          for key2 in user2:
	 	  # key1[0]表示电影id，key1[1]表示对应用的评分
	 	  # 如果是两个用户共同评价的一个应用	
               if key1[1] == key2[1]:
                    sum_x += float(key1[3][:1]) * float(key2[3][:1])
                    sum_y += float(key1[2]) * float(key2[2])
               sum_xy1 += float(key1[3][:1])*float(key1[3][:1])+float(key1[2])*float(key1[2])
               sum_xy2 += float(key2[3][:1])*float(key2[3][:1])+float(key2[2])*float(key2[2])
               sum_xy = math.sqrt(sum_xy1)*math.sqrt(sum_xy2)

     if sum_x+sum_y == 0.0:
          return 0
     demo = math.sqrt(sum_x * sum_y)
     return (sum_x+sum_y) / sum_xy

#使用UserFC进行推荐，输入：应用启动事件序列
def recommendByUserFC(listUser2Score, dictItem2Users, userId, neighbors, timezone):
	 #neighborsTopK存储了相似度和邻居id的倒排表
	 #所以neighbor[1]表示邻居id，neighbor[0]表示相似度
	 #这里的推荐思路是：对于最近k邻居看过的所有电影中的某一电影m
	 #如果m仅仅被一个邻居看过，那么目标用户对此电影的的兴趣度就是目标用户和这个邻居的相似度
	 #如果m被多个邻居看过，那么目标用户对此电影的相似度为目标用户与这些邻居相似度之和
	 #建立推荐字典

     recommand_dict = {}
     for neighbor in neighbors:
          neighbor_user_id = neighbor[1]
		  # 获取应用列表
          apps = getEventList("./cache/app_rec_sys.csv", neighbor_user_id)  

          try:
               # for app in apps:
               if apps[num] not in recommand_dict:
                    recommand_dict[apps[num]] = neighbor[0]
               else:
                    recommand_dict[apps[num]] += neighbor[0]
          except Exception, e:
               pass

	 # 建立推荐列表
     recommand_list = []
     # 遍历从邻居获取的所有安装的应用信息
     for key in recommand_dict:
          # 遍历目标用户的安装信息和邻居安装应用信息作匹配
          for keyApp in listUser2Score[userId]:
               if key and [recommand_dict[key], key] not in recommand_list:
                    # 建立目标用户兴趣度-应用id的倒排表    
                    recommand_list.append([recommand_dict[key], key])
                    
                    
     recommand_list.sort(reverse = True)
	 # listUser2Score列表里边的元素是电影id到电影评分的键值对
	 # 所以这里的k[0]表示一个应用ID，k[1]表示应用评分
     user_apps = [k for k in listUser2Score[userId]]
	 # recommand_list存储的是目标用户兴趣度到电影id的倒排表
	 # 所以这里的的k[1]表示的是应用id，k[0]表示的是兴趣度
     # return [k[1] for k in recommand_list], user_apps, dictItem2Users, neighborsTopK
     return recommand_list, user_apps, dictItem2Users

# userFC计算用户相似度，输入：文件名,用户ID,邻居数量
def userFC(filename, userId, k = 5):
     # 读取文件
     contents = readFile(filename)
     # 文件格式数据转化为二维数组
     rates = getRatingInfo(contents)
     # 标准化数据模块
     # rates = preprocessing.scale(rates)
     # 格式化成字典数据
     listUser2Score, dictItem2Users = getUserScoreDataStructure(rates)
     # print listUser2Score
     # 找邻居
     # 找出最相似的前五个邻居
     neighborsTopK = getNearestNeighbor(userId, listUser2Score, dictItem2Users)[:5]

     return listUser2Score, dictItem2Users, neighborsTopK

# 获取特征矩阵列表
def get_feature():
     # 读取用户相关矩阵数据
     file_matrix = open('./period/md_cache/matrix.txt', 'r')
     try:
          matrix = file_matrix.read()
     finally:
          file_matrix.close()
     # print matrix
     # 将字符串转换为字典格式
     matrix = eval(matrix)

     return matrix

